﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using Odyssey.Controls.Internals;
using System.Windows.Threading;
using System.Windows.Data;
using Odyssey.Utils;

namespace Odyssey.Controls
{
    /// <summary>
    /// DataSource which throttles the total items of an ItemsSource until reaching a specified number of items. It optionally also delays the SelectedItem until it gets exposed.
    /// </summary>
    public class IntervalDataSource : FrameworkElement
    {
        private DispatcherTimer timer;
        private VirtualIntervalCollection collection;
        private bool isLoaded;

        public IntervalDataSource()
            : base()
        {
            timer = new DispatcherTimer();
            timer.Tick += new EventHandler(OnTick);
            Loaded += new RoutedEventHandler(OnLoaded);
            Visibility = System.Windows.Visibility.Collapsed;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            Loaded -= OnLoaded;
            isLoaded = true;
            if (collection != null && StartAfterLoaded) StartTimer();
            //    SetBinding(SelectedItemProperty, new Binding("IntervaledSelectedItem") { Source = this });


        }

        private void OnTick(object sender, EventArgs e)
        {
            var items = Items;
            if (items != null)
            {
                int count = ItemsSource.Count;
                collection.ThresholdCount++;
                if (collection.ThresholdCount > Threshold || count <= collection.ThresholdCount)
                {
                    collection.ThresholdCount = int.MaxValue;
                    StopTimer();
                }
                EnsureSelection();
            }
            else StopTimer();
        }


        public bool IsEnabled
        {
            get { return (bool)GetValue(IsEnabledProperty); }
            set { SetValue(IsEnabledProperty, value); }
        }

        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.Register("IsEnabled", typeof(bool), typeof(IntervalDataSource), new PropertyMetadata(true));


        public bool StartAfterLoaded
        {
            get { return (bool)GetValue(StartAfterLoadedProperty); }
            set { SetValue(StartAfterLoadedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StartAfterLoaded.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartAfterLoadedProperty =
            DependencyProperty.Register("StartAfterLoaded", typeof(bool), typeof(IntervalDataSource), new PropertyMetadata(false));




        public int StartCount
        {
            get { return (int)GetValue(InitialLimitProperty); }
            set { SetValue(InitialLimitProperty, value); }
        }

        public static readonly DependencyProperty InitialLimitProperty =
            DependencyProperty.Register("StartCount", typeof(int), typeof(IntervalDataSource), new PropertyMetadata(0));



        /// <summary>
        /// Gets or sets the threshold of items until to stop the interval.
        /// </summary>
        public int Threshold
        {
            get { return (int)GetValue(ThresholdProperty); }
            set { SetValue(ThresholdProperty, value); }
        }

        public static readonly DependencyProperty ThresholdProperty =
            DependencyProperty.Register("Threshold", typeof(int), typeof(IntervalDataSource), new PropertyMetadata(10));


        /// <summary>
        /// Gets or sets the interval between counting forward.
        /// </summary>
        public TimeSpan Interval
        {
            get { return (TimeSpan)GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }

        public static readonly DependencyProperty IntervalProperty =
            DependencyProperty.Register("Interval", typeof(TimeSpan), typeof(IntervalDataSource), new PropertyMetadata(TimeSpan.FromMilliseconds(1)));



        /// <summary>
        /// Gets or sets the items to forward.
        /// </summary>
        public IList ItemsSource
        {
            get { return (IList)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IList), typeof(IntervalDataSource), new PropertyMetadata(null, OnItemsSourceChanged));


        private static void OnItemsSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
        {
            (o as IntervalDataSource).OnItemsSourceChanged(args.OldValue as IList, args.NewValue as IList);
        }

        private void OnItemsSourceChanged(IList oldValue, IList newValue)
        {
            StopTimer();
            if (collection != null)
            {
                collection.Dispose();
            }
            bool isEnabled = IsEnabled;
            collection = newValue != null ? new VirtualIntervalCollection(newValue, isEnabled ? StartCount : int.MaxValue) : null;
            Items = collection;
            if (newValue != null)
            {
                collection.CollectionChanged += (s, e) =>
                    {
                        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
                        {
                            StopTimer();
                            StartTimer();
                        }
                    };
                if (isEnabled && (isLoaded || !StartAfterLoaded)) StartTimer();
            }
        }

        private void StopTimer()
        {
            timer.Stop();
        }

        private void StartTimer()
        {
            collection.ThresholdCount = StartCount;
            timer.Interval = this.Interval;
            timer.Start();
        }


        /// <summary>
        /// Gets the list of intervaled items.
        /// </summary>
        public IList Items
        {
            get { return (IList)GetValue(ItemsProperty); }
            private set { SetValue(ItemsProperty, value); }
        }

        public static readonly DependencyProperty ItemsProperty =
            DependencyProperty.Register("Items", typeof(IList), typeof(IntervalDataSource), new PropertyMetadata(null));




        /// <summary>
        /// Gets or sets the selected item which is exposed as IntervaledSelectedItem when it gets exposed in Items.
        /// This property is usually bound to a ViewModel.
        /// </summary>
        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(IntervalDataSource), new PropertyMetadata(null, OnSelectedItemChanged));

        private static void OnSelectedItemChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
        {
            (o as IntervalDataSource).OnSelectedItemChanged(args.OldValue, args.NewValue);
        }

        private void OnSelectedItemChanged(object oldValue, object newValue)
        {
            if (newValue != null)
            {
              //  EnsureSelection();
            }
            else
            {
                IntervaledSelectedItem = null;
            }
        }


        private void EnsureSelection()
        {
            var items = ItemsSource;
            if (items != null)
            {
                object selected = SelectedItem;
                if (selected != null && IntervaledSelectedItem == null)
                {
                    int index = items.IndexOf(selected);
                    if (index < collection.ThresholdCount)
                    {
                        VisualTreeExtensions.ScheduleOnNextRender(() => IntervaledSelectedItem = selected);
                        //Dispatcher.BeginInvoke(delegate
                        //{
                        //    IntervaledSelectedItem = selected;
                        //});
                    }
                }
            }
        }



        /// <summary>
        /// Gets or sets the SelectedItem which is set when the intervaled collection exposes the specified SelectedItem.
        /// This Property is usually used to be bound as SelectedItem of a Selector control.
        /// </summary>
        public object IntervaledSelectedItem
        {
            get { return (object)GetValue(IntervaledSelectedItemProperty); }
            set { SetValue(IntervaledSelectedItemProperty, value); }
        }

        public static readonly DependencyProperty IntervaledSelectedItemProperty =
            DependencyProperty.Register("IntervaledSelectedItem", typeof(object), typeof(IntervalDataSource), new PropertyMetadata(null, OnIntervaledSelectedItemChanged));


        private static void OnIntervaledSelectedItemChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
        {
            //   IntervalDataSource ds = o as IntervalDataSource;
            //ds.SetBinding(SelectedItemProperty, new Binding("IntervaledSelectedItem") { Source = ds });
            o.SetValue(SelectedItemProperty, args.NewValue);
        }


    }
}
